package nig.mf.plugin.sink;

import arc.mf.plugin.dtype.DataType;
import arc.mf.plugin.dtype.IntegerType;
import arc.mf.plugin.dtype.PasswordType;
import arc.mf.plugin.dtype.StringType;
import arc.mf.plugin.sink.ParameterDefinition;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;


/**
 * Common parameters for remote sinks. It's up to each custom sink to decide
 * which ones are mandatory and which ones are optional.<b>
 * 
 * Note: Individual enums are intentionally named using lowerCamelCase so each
 * one's {@link #name()} corresponds to the actual parameter name that's
 * supplied to Mediaflux.
 * 
 * @author King Lung Chiu
 */
public enum RemoteSinkParam {

   // global settings
   host(
      StringType.DEFAULT,
      "remote host address"),
   port(
      new IntegerType(1, 65535),
      "remote port"),
   hostFingerprint(
      StringType.DEFAULT,
      "remote host key fingerprint"),
   path(
      StringType.DEFAULT,
      "destination path"),
//   baseDir(
//      StringType.DEFAULT,
//      "destination base dir"),
//   buffSize(
//      new IntegerType(Default.MIN_BUFF_SIZE, Integer.MAX_VALUE),
//      "transfer buffer size (bytes), defaults to the minimum value " + Default.MIN_BUFF_SIZE),

   // per-user settings
   username(
      StringType.DEFAULT,
      "username to access remote host"),
   privateKey(
      PasswordType.DEFAULT,
      "private key to access remote host"),
//   fullDestDir(
//      StringType.DEFAULT,
//      String.format("absolute path on destination (supercedes %s)", baseDir.name()))
   ;

   public final ParameterDefinition def;
   RemoteSinkParam(DataType t, String desc) {
      def = new ParameterDefinition(t, desc);
   }
   
   public static Map<String, ParameterDefinition> definitions() {
      return Collections.unmodifiableMap(paramDefs);
   }

   /**
    * Tests whether this parameter is supplied.
    * 
    * @param params
    *    The map to check existence of this parameter.
    * 
    * @return
    *    True if the map contains this parameter, false otherwise.
    */
   public boolean existsIn(Map<String, String> params) {
      return (params != null) && (params.containsKey(name()));
   }

   /**
    * Tests whether all required parameters are supplied.
    * 
    * @param params
    *    The map to check existence of the required parameters.
    * 
    * @param enums
    *    The actual RemoteSinkParameters (ie. "required parameters") to look for.
    * 
    * @return
    *    True if the map contains all the required parameters, false otherwise.
    *    Note: method always returns false when the map is null or empty.
    */
   public static boolean existIn(
      Map<String, String> params,
      RemoteSinkParam... enums
   ) {
      if(params == null || params.isEmpty())
         return false;

      boolean allExist = true;
      for(RemoteSinkParam p: enums) {
         allExist &= p.existsIn(params);
      }
      return allExist;
   }

   /**
    * Retrieves the user-supplied value for this parameter.
    * 
    * @param params
    *    The map to extract the value from.
    * 
    * @return
    *    The user-supplied value for this parameter, or an empty string if it's
    *    not supplied.
    */
   public String from(Map<String, String> params) {
      return this.existsIn(params)? params.get(name()) : "";
   }

//   public static int buffSizeFrom(Map<String, String> params) {
//      try {
//         int supplied = Integer.parseInt(buffSize.from(params));
//         if(supplied > Default.MIN_BUFF_SIZE)
//            return supplied;
//      } catch(NumberFormatException e) {
//         // do nothing: fall back to default
//      }
//      return Default.MIN_BUFF_SIZE;
//   }

   public static Short portFrom(Map<String, String> params) {
      try {
         int supplied = Integer.parseInt(port.from(params));
         return (short) supplied;
      } catch(NumberFormatException e) {
         // do nothing: fall back to default
      }
      return null;
   }

//      private static final Map<String, Type> paramDefs = new HashMap<>();
   private static final Map<String, ParameterDefinition> paramDefs =
      new HashMap<String, ParameterDefinition>();
   static {
      for (RemoteSinkParam p : values()) {
         paramDefs.put(p.name(), p.def);
      }
   }
   
//   public static final class Default {
//      static final int MIN_BUFF_SIZE = 20480;
//   }
}